#include "http_server.h"
#include "index_html.h"
#include "favicon_ico.h"
#include "wifi_execute.h"
#include "file_op.h"
#include "configs.h"
#include "temperature.h"
#include "io_out.h"
#include "light.h"
#include "buzzer.h"
#include "rtc_op.h"
#include "control_logic.h"
#include <cJSON.h>

#define SERVER_PORT 80

const static char http_html_hdr[] = "HTTP/1.1 200 OK\r\nContent-type: text/html\r\n\r\n";
const static char http_icon_hdr[] = "HTTP/1.1 200 OK\r\nContent-type: image/x-icon\r\n\r\n";
const static char http_json_hdr[] = "HTTP/1.1 200 OK\r\nContent-type: application/json\r\n\r\n";

static char *get_post_payload(const char *data, uint32_t len) 
{
    return strstr(data, "\r\n\r\n") + 4;
}

static void web_http_server(struct netconn *conn)
{
    struct netbuf *inputbuf;
    char *buf = NULL;
    uint16_t dat_size = 0;
    u16_t buflen = 0;
    err_t err = ERR_OK;
    char *token;
    char *raw_data;

    vTaskDelay(50);

    while ((err == ERR_OK) && (conn->recv_avail > 0)) {
        err = netconn_recv(conn, &inputbuf);
        if (err == ERR_OK) {
            netbuf_data(inputbuf, (void **)&raw_data, &buflen);
            buf = realloc(buf, dat_size + buflen);
            memcpy(buf + dat_size, raw_data, buflen);
            dat_size += buflen;
        }
        netbuf_delete(inputbuf);
        vTaskDelay(20);
    }

    if (dat_size > 0) {
        raw_data = malloc(dat_size + 1);
        memset(raw_data, 0, dat_size + 1);
        memcpy(raw_data, buf, dat_size);
        err = ERR_OK;
    }

    if (err == ERR_OK) {
        if (dat_size >=5) {
            token = strtok(buf, "\r\n");
            token = strtok(token, " ");
            if (strcmp(token, "GET") == 0) { /* Judge if this is an HTTP GET command */
                token = strtok(NULL, " ");
                printf("HTTP GET Request path: %s\r\n", token);
                if ((strcmp(token, "/") == 0) || (strcmp(token, "/index.html") == 0) || (strcmp(token, "/index.htm") == 0)) { // index.html
                    netconn_write(conn, http_html_hdr, sizeof(http_html_hdr) - 1, NETCONN_NOCOPY);
                    netconn_write(conn, index_html, sizeof(index_html) - 1, NETCONN_NOCOPY);
                } else if (strcmp(token, "/scanWifi") == 0) {
                    uint32_t wlan_num;
                    wifi_mgmr_ap_item_t *ap_ary = NULL;
                    wifi_mgmr_all_ap_scan(&ap_ary, &wlan_num);

                    cJSON* wifi_res = NULL;
                    wifi_res = cJSON_CreateArray();
                    for (int i = 0; i < wlan_num; i++) {
                        cJSON_AddItemToArray(wifi_res, cJSON_CreateString(ap_ary[i].ssid));
                    }
                    char *str = cJSON_Print(wifi_res);
                    netconn_write(conn, http_json_hdr, strlen(http_json_hdr), NETCONN_NOCOPY);
                    netconn_write(conn, str, strlen(str), NETCONN_NOCOPY);

                    cJSON_Delete(wifi_res);
                    free(str);
                    free(ap_ary);
                } else if (strcmp(token, "/favicon.ico") == 0) {
                    netconn_write(conn, http_icon_hdr, strlen(http_icon_hdr), NETCONN_NOCOPY);
                    netconn_write(conn, favicon_ico, sizeof(favicon_ico), NETCONN_NOCOPY);
                } else if (strcmp(token, "/temperature") == 0) {
                    cJSON* temp = cJSON_CreateObject();
                    cJSON_AddNumberToObject(temp, "temperature", get_temperature());
                    cJSON_AddNumberToObject(temp, "humidity", get_humidity());
                    char *str = cJSON_Print(temp);
                    netconn_write(conn, http_json_hdr, strlen(http_json_hdr), NETCONN_NOCOPY);
                    netconn_write(conn, str, strlen(str), NETCONN_NOCOPY);
                } else if (strcmp(token, "/configs") == 0) {
                    char config_buf[1024];
                    memset(config_buf, 0, 1024);
                    read_file(CONFIGS_FILE, config_buf, 1024);
                    netconn_write(conn, http_json_hdr, strlen(http_json_hdr), NETCONN_NOCOPY);
                    netconn_write(conn, config_buf, strlen(config_buf), NETCONN_NOCOPY);
                } else if (strcmp(token, "/timers") == 0) {
                    char *timers_buf = malloc(8192);
                    memset(timers_buf, 0, 8192);
                    read_file(TIMERS_FILE, timers_buf, 8192);
                    netconn_write(conn, http_json_hdr, strlen(http_json_hdr), NETCONN_NOCOPY);
                    netconn_write(conn, timers_buf, strlen(timers_buf), NETCONN_NOCOPY);
                    free(timers_buf);
                } else if (strcmp(token, "/time-RTC") == 0) {
                    char rtc_buf[16];
                    memset(rtc_buf, 0, 16);
                    snprintf(rtc_buf, 16, "%llu", get_rtc_time_stamp());
                    netconn_write(conn, http_json_hdr, strlen(http_json_hdr), NETCONN_NOCOPY);
                    netconn_write(conn, rtc_buf, strlen(rtc_buf), NETCONN_NOCOPY);
                } else if (strcmp(token, "/getBrightness") == 0) {
                    char buf[4];
                    memset(buf, 0, 4);
                    snprintf(buf, 4, "%u", get_light_brightness());
                    netconn_write(conn, http_json_hdr, strlen(http_json_hdr), NETCONN_NOCOPY);
                    netconn_write(conn, buf, strlen(buf), NETCONN_NOCOPY);
                } else if (strcmp(token, "/getVolume") == 0) {
                    char buf[4];
                    memset(buf, 0, 4);
                    snprintf(buf, 4, "%u", get_buzzer_volume());
                    netconn_write(conn, http_json_hdr, strlen(http_json_hdr), NETCONN_NOCOPY);
                    netconn_write(conn, buf, strlen(buf), NETCONN_NOCOPY);
                } else {
                    netconn_write(conn, http_html_hdr, sizeof(http_html_hdr) - 1, NETCONN_NOCOPY);
                    netconn_write(conn, index_html, sizeof(index_html) - 1, NETCONN_NOCOPY);
                }
            } else if (strcmp(token, "POST") == 0) {
                char *path = strtok(NULL, " ");
                printf("HTTP POST Request path: %s\r\n", path);
                if (strcmp(path, "/setWifi") == 0) {
                    char *payload = get_post_payload(raw_data, dat_size);
                    cJSON *respJson = cJSON_Parse(payload);
                    cJSON *ssid = cJSON_GetObjectItem(respJson, "ssid");
                    cJSON *password = cJSON_GetObjectItem(respJson, "password");

                    char *tmp_ssid = malloc(strlen(ssid->valuestring) + 1);
                    memset(tmp_ssid, 0, strlen(ssid->valuestring) + 1);
                    memcpy(tmp_ssid, ssid->valuestring, strlen(ssid->valuestring));
                    char *tmp_password = malloc(strlen(password->valuestring) + 1);
                    memset(tmp_password, 0, strlen(password->valuestring) + 1);
                    memcpy(tmp_password, password->valuestring, strlen(password->valuestring));
                    set_sta(tmp_ssid, tmp_password); // WARNING: 内存泄露

                    char buf[1024];
                    memset(buf, 0, 1024);
                    int ret = read_file(CONFIGS_FILE, buf, 1024);
                    if (ret > 0) {
                        cJSON *config = cJSON_Parse(buf);
                        cJSON_ReplaceItemInObject(config, "ssid", cJSON_CreateString(ssid->valuestring));
                        cJSON_ReplaceItemInObject(config, "password", cJSON_CreateString(password->valuestring));
                        char *str = cJSON_PrintUnformatted(config);
                        write_file(CONFIGS_FILE, str, strlen(str));
                        cJSON_Delete(config);
                        free(str);
                    } else {
                        write_file(CONFIGS_FILE, payload, strlen(payload));
                    }
                    cJSON_Delete(respJson);
                    netconn_write(conn, http_html_hdr, sizeof(http_html_hdr) - 1, NETCONN_NOCOPY);
                } else if (strcmp(path, "/setTimer") == 0) {
                    char *payload = get_post_payload(raw_data, dat_size);
                    write_file(TIMERS_FILE, payload, strlen(payload)+1);
                    netconn_write(conn, http_html_hdr, sizeof(http_html_hdr) - 1, NETCONN_NOCOPY);
                    load_ctrl_logic_configs();
                } else if (strcmp(path, "/setAutoCtrl") == 0) {
                    netconn_write(conn, http_html_hdr, sizeof(http_html_hdr) - 1, NETCONN_NOCOPY);
                    set_manual_ctrl(0);
                } else if (strcmp(path, "/relay") == 0) {
                    char *payload = get_post_payload(raw_data, dat_size);
                    cJSON *control = cJSON_Parse(payload);
                    cJSON *channel = cJSON_GetObjectItem(control, "channel");
                    cJSON *value = cJSON_GetObjectItem(control, "connect");
                    cJSON_Delete(control);
                    io_out(channel->valueint, value->valueint);
                    set_manual_ctrl(1);
                    netconn_write(conn, http_html_hdr, sizeof(http_html_hdr) - 1, NETCONN_NOCOPY);
                } else if (strcmp(path, "/setBrightness") == 0) {
                    char *payload = get_post_payload(raw_data, dat_size);
                    set_light_brightness(atoi(payload));
                    netconn_write(conn, http_html_hdr, sizeof(http_html_hdr) - 1, NETCONN_NOCOPY);
                } else if (strcmp(path, "/setVolume") == 0) {
                    char *payload = get_post_payload(raw_data, dat_size);
                    set_buzzer_volume(atoi(payload));
                    netconn_write(conn, http_html_hdr, sizeof(http_html_hdr) - 1, NETCONN_NOCOPY);
                } else if (strcmp(path, "/setRTC") == 0) {
                    char *payload = get_post_payload(raw_data, dat_size);
                    set_rtc_time_stamp(atoll(payload));
                    netconn_write(conn, http_html_hdr, sizeof(http_html_hdr) - 1, NETCONN_NOCOPY);
                }
            }
        }

        free(buf);
    }
    netconn_close(conn);
    // netbuf_delete(inputbuf);
}

void http_server_start(void *pvParameters)
{
    struct netconn *conn, *newconn;
    err_t err;
    conn = netconn_new(NETCONN_TCP);
    netconn_bind(conn, NULL, SERVER_PORT);
    netconn_listen(conn);
    
    while (1) {
        err = netconn_accept(conn, &newconn);
        if (err == ERR_OK) {
            web_http_server(newconn);
            netconn_delete(newconn);
        } else {
            netconn_close(conn);
            netconn_delete(conn);
            break;
        }
    }
}
